import torch

# =========================== grad_tensors =====================================
# w = torch.tensor([1.], requires_grad=True)  # 梯度为1
# x = torch.tensor([2.], requires_grad=True)
# a = torch.add(w, x)  # retain_grad()保留梯度
# # a.retain_grad()
# b = torch.add(w, 1)
# y0 = torch.mul(a, b)  # y0 = (x+w) * (w+1) = 6
# y1 = torch.add(a, b)  # y1 = (x+w) + (w+1) = 5    dy1/dw = 2
#
# loss = torch.cat([y0, y1], dim=0)   # tensor([6., 5.])
# grad_tenors = torch.tensor([1., 2.])    # 多个梯度中权重的设置，y0对应1，y1对应2
# # gradient 传入 torch.autograd.backward()中的grad_tensors
# loss.backward(gradient=grad_tenors)
#
# print(loss)
# print(w.grad)  # 9 = 1*5 + 2*2

# =========================== autograd.grad ====================================

# x = torch.tensor([3.], requires_grad=True)
# y = torch.pow(x, 2)  # y = x**2
#
# grad_1 = torch.autograd.grad(y, x, create_graph=True)
# # grad_1 = dy/dx = 2x = 2 * 3 = 6
# print(grad_1)  # (tensor([6.], grad_fn=<MulBackward0>),)
#
# print(grad_1[0])  # tensor([6.], grad_fn=<MulBackward0>)
# grad_2 = torch.autograd.grad(grad_1[0], x)  # 求二阶导
# # grad_2 = d(dy/dx)/dx = d(2x)/dx = 2
# print(grad_2)   # (tensor([2.]),)

# =========================== tip1:梯度不自动清零 =====================================
# w = torch.tensor([1.], requires_grad=True)
# x = torch.tensor([2.], requires_grad=True)
# for i in range(4):
#     a = torch.add(w, x)
#     b = torch.add(w, 1)
#     y = torch.mul(a, b)
#     y.backward()
#     print(w.grad)
#
#     w.grad.zero_()  # 手动对梯度进行清零，'_'：原位操作。
# tensor([5.])
# tensor([5.])
# tensor([5.])
# tensor([5.])

# =========================== tip2:依赖于叶子节点 =====================================
# w = torch.tensor([1.], requires_grad=True)
# x = torch.tensor([2.], requires_grad=True)
# a = torch.add(w, x)
# b = torch.add(w, 1)
# y = torch.mul(a, b)
#
# print(a.requires_grad, b.requires_grad, y.requires_grad)
# True True True

# =========================== tip3:叶子节点不可执行in-place =====================================
# a = torch.ones((1, ))
# print(id(a), a)
# # 1970317858920 tensor([1.])
#
# a = a + torch.ones((1, ))
# print(id(a), a)
# # 1970369990728 tensor([2.])
# # 开辟了新的地址，就不是原位操作，
#
# a += torch.ones((1, ))  # 原位操作.在原始地址上改变
# print(id(a), a)
# 1970369990728 tensor([3.])


flag = True
# flag = False
if flag:

    w = torch.tensor([1.], requires_grad=True)
    x = torch.tensor([2.], requires_grad=True)

    a = torch.add(w, x)
    b = torch.add(w, 1)
    y = torch.mul(a, b)

    w.add_(1)
    """
    autograd小贴士：
        梯度不自动清零 
        依赖于叶子结点的结点，requires_grad默认为True     
        叶子结点不可执行in-place 
    """
    y.backward()  # 报错















